1//////////////////////////////////////////////////////////////////////
  2// LibFile: compat.scad
  3//   Backwards Compatability library
  4//   To use, include this line at the top of your file:
  5//   ```
  6//   use <compat.scad>
  7//   ```
  8//////////////////////////////////////////////////////////////////////
  9
 10
 11/*
 12BSD 2-Clause License
 13
 14Copyright (c) 2017, Revar Desmera
 15All rights reserved.
 16
 17Redistribution and use in source and binary forms, with or without
 18modification, are permitted provided that the following conditions are met:
 19
 20* Redistributions of source code must retain the above copyright notice, this
 21  list of conditions and the following disclaimer.
 22
 23* Redistributions in binary form must reproduce the above copyright notice,
 24  this list of conditions and the following disclaimer in the documentation
 25  and/or other materials provided with the distribution.
 26
 27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 28AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 29IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 30DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 31FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 32DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 33SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 34CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 35OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 36OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 37*/
 38
 39
 40// Section: Functions
 41
 42
 43// Function: default()
 44// Description:
 45//   Returns the value given as `v` if it is not `undef`.
 46//   Otherwise, returns the value of `dflt`.
 47// Arguments:
 48//   v = Value to pass through if not `undef`.
 49//   dflt = Value to return if `v` *is* `undef`.
 50function default(v,dflt=undef) = v==undef? dflt : v;
 51
 52
 53// Function: is_def()
 54// Description: Returns true if given value is not `undef`.
 55function is_def(v) = (version_num() > 20190100)? !is_undef(v) : (v != undef);
 56
 57
 58// Function: is_str()
 59// Description: Given a value, returns true if it is a string.
 60function is_str(v) = (version_num() > 20190100)? is_string(v) : (v=="" || (is_def(v) && is_def(v[0]) && (len(str(v,v)) == len(v)*2)));
 61
 62
 63// Function: is_boolean()
 64// Description: Given a value, returns true if it is a boolean.
 65function is_boolean(v) = (version_num() > 20190100)? is_bool(v) : (!is_str(v) && (str(v) == "true" || str(v) == "false"));
 66
 67
 68// Function: is_scalar()
 69// Description: Given a value, returns true if it is a scalar number.
 70function is_scalar(v) = (version_num() > 20190100)? is_num(v) : (!is_boolean(v) && is_def(v+0));
 71
 72
 73// Function: is_array()
 74// Description: Given a value, returns true if it is an array/list/vector.
 75function is_array(v) = (version_num() > 20190100)? is_list(v) : (v==[] || (is_def(v[0]) && !is_str(v) ));
 76
 77
 78// Function: get_radius()
 79// Description:
 80//   Given various radii and diameters, returns the most specific radius.
 81//   If a diameter is most specific, returns half its value, giving the radius.
 82//   If no radii or diameters are defined, returns the value of dflt.
 83//   Value specificity order is r1, d1, r, d, then dflt
 84// Arguments:
 85//   r1 = Most specific radius.
 86//   d1 = Most specific Diameter.
 87//   r = Most general radius.
 88//   d = Most general diameter.
 89//   dflt = Value to return if all other values given are `undef`.
 90function get_radius(r1=undef, r=undef, d1=undef, d=undef, dflt=undef) = (
 91	is_def(r1)? r1 :
 92	is_def(d1)? d1/2 :
 93	is_def(r)? r :
 94	is_def(d)? d/2 :
 95	dflt
 96);
 97
 98
 99// Function: Len()
100// Description:
101//   Given an array, returns number of items in array.  Otherwise returns `undef`.
102function Len(v) = is_array(v)? len(v) : undef;
103
104
105// Function: remove_undefs()
106// Description: Removes all `undef`s from a list.
107function remove_undefs(v) = [for (x = v) if (is_def(x)) x];
108
109
110// Function: first_defined()
111// Description:
112//   Returns the first item in the list that is not `undef`.
113//   If all items are `undef`, or list is empty, returns `undef`.
114function first_defined(v) = remove_undefs(v)[0];
115
116
117// Function: any_defined()
118// Description:
119//   Returns true if any item in the given array is not `undef`.
120function any_defined(v) = len(remove_undefs(v))>0;
121
122
123// Function: scalar_vec3()
124// Usage:
125//   scalar_vec3(v, [dflt]);
126// Description:
127//   If `v` is a scalar, and `dflt==undef`, returns `[v, v, v]`.
128//   If `v` is a scalar, and `dflt!=undef`, returns `[v, dflt, dflt]`.
129//   If `v` is a vector, returns the first 3 items, with any missing values replaced by `dflt`.
130//   If `v` is `undef`, returns `undef`.
131// Arguments:
132//   v = Value to return vector from.
133//   dflt = Default value to set empty vector parts from.
134function scalar_vec3(v, dflt=undef) =
135	!is_def(v)? undef :
136	is_array(v)? [for (i=[0:2]) default(v[i], default(dflt, 0))] :
137	is_def(dflt)? [v,dflt,dflt] : [v,v,v];
138
139
140
141// Function: f_echo()
142// Description: If possible, echo a message from a function.
143function f_echo(msg) = (version_num() > 20190100)? echo(msg) : 0;
144
145
146// Section: Modules
147
148
149// Module: assert_in_list()
150// Usage:
151//   assert_in_list(argname, val, l, [idx]);
152// Description:
153//   Emulates the newer OpenSCAD `assert()` with an `in_list()` test.
154//   You can also use this as a function call from a function.
155// Arguments:
156//   argname = The name of the argument value being tested.
157//   val = The value to test if it exists in the list.
158//   l = The list to look for `val` in.
159//   idx = If given, and `l` is a list of lists, look for `val` in the given index of each sublist.
160module assert_in_list(argname, val, l, idx=undef) {
161	succ = search([val], l, num_returns_per_match=1, index_col_num=idx) != [[]];
162	if (!succ) {
163		msg = str(
164			"In argument '", argname, "', ",
165			(is_str(val)? str("\"", val, "\"") : val),
166			" must be one of ",
167			(is_def(idx)? [for (v=l) v[idx]] : l)
168		);
169		assertion(succ, msg);
170	}
171}
172
173function assert_in_list(argname, val, l, idx=undef) =
174	let(succ = search([val], l, num_returns_per_match=1, index_col_num=idx) != [[]])
175	succ? 0 : let(
176		msg = str(
177			"In argument '", argname, "', ",
178			(is_str(val)? str("\"", val, "\"") : val),
179			" must be one of ",
180			(is_def(idx)? [for (v=l) v[idx]] : l)
181		)
182	) assertion(succ, msg);
183
184
185// Module: assertion()
186// Usage:
187//   assertion(succ, msg);
188// Description:
189//   Backwards compatible assert() semi-replacement.
190//   If `succ` is false, then print an error with `msg`.
191//   You can also use this as a function call from a function.
192// Arguments:
193//   succ = If this is `false`, trigger the assertion.
194//   msg = The message to emit if `succ` is `false`.
195module assertion(succ, msg) {
196	if (version_num() > 20190100) {
197		// assert() will echo the variable name, and `succ` looks confusing there.  So we store it in FAILED.
198		FAILED = succ;
199		assert(FAILED, msg);
200	} else if (!succ) {
201		echo_error(msg);
202	}
203}
204
205function assertion(succ, msg) =
206	(version_num() > 20190100)? let(FAILED=succ) assert(FAILED, msg) : 0;
207
208
209// Module: echo_error()
210// Usage:
211//   echo_error(msg, [pfx]);
212// Description:
213//   Emulates printing of an error message.  The text will be shaded red.
214//   You can also use this as a function call from a function.
215// Arguments:
216//   msg = The message to print.
217//   pfx = The prefix to print before `msg`.  Default: `ERROR`
218module echo_error(msg, pfx="ERROR") {
219	echo(str("<p style=\"background-color: #ffb0b0\"><b>", pfx, ":</b> ", msg, "</p>"));
220}
221
222function echo_error(msg, pfx="ERROR") =
223	f_echo(str("<p style=\"background-color: #ffb0b0\"><b>", pfx, ":</b> ", msg, "</p>"));
224
225
226// Module: echo_warning()
227// Usage:
228//   echo_warning(msg, [pfx]);
229// Description:
230//   Emulates printing of a warning message.  The text will be shaded yellow.
231//   You can also use this as a function call from a function.
232// Arguments:
233//   msg = The message to print.
234//   pfx = The prefix to print before `msg`.  Default: `WARNING`
235module echo_warning(msg, pfx="WARNING") {
236	echo(str("<p style=\"background-color: #ffffb0\"><b>", pfx, ":</b> ", msg, "</p>"));
237}
238
239function echo_warning(msg, pfx="WARNING") =
240	f_echo(str("<p style=\"background-color: #ffffb0\"><b>", pfx, ":</b> ", msg, "</p>"));
241
242
243// Module: deprecate()
244// Usage:
245//   deprecate(name, [suggest]);
246// Description:
247//   Show module deprecation warnings.
248//   You can also use this as a function call from a function.
249// Arguments:
250//   name = The name of the module that is deprecated.
251//   suggest = If given, the module to recommend using instead.
252module deprecate(name, suggest=undef) {
253	echo_warning(pfx="DEPRECATED",
254		str(
255			"`<code>", name, "</code>` is deprecated and should not be used.",
256			!is_def(suggest)? "" : str(
257				"  You should use `<code>", suggest, "</code>` instead."
258			)
259		)
260	);
261}
262
263function deprecate(name, suggest=undef) =
264	echo_warning(pfx="DEPRECATED",
265		str(
266			"`<code>", name, "</code>` is deprecated and should not be used.",
267			!is_def(suggest)? "" : str(
268				"  You should use `<code>", suggest, "</code>` instead."
269			)
270		)
271	);
272
273
274// Module: deprecate_argument()
275// Usage:
276//   deprecate(name, arg, [suggest]);
277// Description:
278//   Show argument deprecation warnings.
279//   You can also use this as a function call from a function.
280// Arguments:
281//   name = The name of the module/function the deprecated argument is used in.
282//   arg = The name of the deprecated argument.
283//   suggest = If given, the argument to recommend using instead.
284module deprecate_argument(name, arg, suggest=undef) {
285	echo_warning(pfx="DEPRECATED ARG", str(
286		"In `<code>", name, "</code>`, ",
287		"the argument `<code>", arg, "</code>` ",
288		"is deprecated and should not be used.",
289		!is_def(suggest)? "" : str(
290			"  You should use `<code>", suggest, "</code>` instead."
291		)
292	));
293}
294
295function deprecate_argument(name, arg, suggest=undef) =
296	echo_warning(pfx="DEPRECATED ARG", str(
297		"In `<code>", name, "</code>`, ",
298		"the argument `<code>", arg, "</code>` ",
299		"is deprecated and should not be used.",
300		!is_def(suggest)? "" : str(
301			"  You should use `<code>", suggest, "</code>` instead."
302		)
303	));
304
305
306
307// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap